﻿using Singer.Core;
using Singer.Shared.UserContext;
using System.Linq.Expressions;

namespace Singer.Middleware.FreeSql;

/// <summary>
/// FreeSql 仓储基类
/// </summary>
/// <typeparam name="TEntity">实体模型</typeparam>
public abstract class BaseRepository<TEntity> : global::FreeSql.BaseRepository<TEntity>, IBaseRepository<TEntity>
    where TEntity : class, IEntity
{
    bool IsSoftDelete => typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity));
    IUserContextAccessor _userContextAccessor;
    IUserContext? _userContext => _userContextAccessor.UserContext;
    public BaseRepository(IFreeSql fsql, IUserContextAccessor userContextAccessor, Expression<Func<TEntity, bool>>? filter = null, Func<string, string>? asTable = null) : base(fsql, filter, asTable)
    {
        _userContextAccessor = userContextAccessor;
    }

    private SolftDeleteDto CurrentSolftDeleteDto => new SolftDeleteDto(_userContext);

    public override int Delete(Expression<Func<TEntity, bool>> predicate)
    {
        if (predicate == null)
            throw new Exception("【FreeSql】方法'Delete' 参数'predicate(条件表达式)'不可为空.");
        if (!IsSoftDelete)
            return base.Delete(predicate);
        return Orm.Update<TEntity>().Where(predicate).SetDto(CurrentSolftDeleteDto).ExecuteAffrows();
    }

    public override int Delete(IEnumerable<TEntity> entitys)
    {
        if (!IsSoftDelete)
            return base.Delete(entitys);
        return Orm.Update<TEntity>(entitys).SetDto(CurrentSolftDeleteDto).ExecuteAffrows();
    }

    public override int Delete(TEntity entity)
    {
        if (!IsSoftDelete)
            return base.Delete(entity);
        return Orm.Update<TEntity>(entity).SetDto(CurrentSolftDeleteDto).ExecuteAffrows();
    }

    public override Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
    {
        if (predicate == null)
            throw new Exception("【FreeSql】方法'DeleteAsync' 参数'predicate(条件表达式)'不可为空.");
        if (!IsSoftDelete)
            return base.DeleteAsync(predicate, cancellationToken);
        return Orm.Update<TEntity>().Where(predicate).SetDto(CurrentSolftDeleteDto).ExecuteAffrowsAsync(cancellationToken);
    }

    public override Task<int> DeleteAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default)
    {
        if (!IsSoftDelete)
            return base.DeleteAsync(entitys, cancellationToken);
        return Orm.Update<TEntity>(entitys).SetDto(CurrentSolftDeleteDto).ExecuteAffrowsAsync();
    }

    public override Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default)
    {
        if (!IsSoftDelete)
            return base.DeleteAsync(entity, cancellationToken);
        return Orm.Update<TEntity>(entity).SetDto(CurrentSolftDeleteDto).ExecuteAffrowsAsync(cancellationToken);
    }

    public bool Any(Expression<Func<TEntity, bool>>? exp = null)
    {
        if (exp == null)
            exp = x => true;
        return Orm.Select<TEntity>().Any(exp);
    }

    public Task<bool> AnyAsync(Expression<Func<TEntity, bool>>? exp = null, CancellationToken cancellationToken = default)
    {
        if (exp == null)
            exp = x => true;
        return Orm.Select<TEntity>().AnyAsync(exp, cancellationToken);
    }

    public TEntity? Get(Expression<Func<TEntity, bool>>? exp = null)
    {
        if (exp == null)
            exp = x => true;
        return Orm.Select<TEntity>().Where(exp).First();
    }

    public Task<TEntity?> GetAsync(Expression<Func<TEntity, bool>>? exp = null, CancellationToken cancellationToken = default)
    {
        if (exp == null)
            exp = x => true;
        return Orm.Select<TEntity>().Where(exp).FirstAsync(cancellationToken);
    }

    public TDto? Get<TDto>(Expression<Func<TEntity, bool>>? exp = null) where TDto : class
    {
        if (exp == null)
            exp = x => true;
        return Orm.Select<TEntity>().Where(exp).First<TDto>();
    }

    public Task<TDto?> GetAsync<TDto>(Expression<Func<TEntity, bool>>? exp = null, CancellationToken cancellationToken = default) where TDto : class
    {
        if (exp == null)
            exp = x => true;
        return Orm.Select<TEntity>().Where(exp).FirstAsync<TDto>(cancellationToken);
    }

    private class SolftDeleteDto
    {
        public string? DeleterId { get; set; }
        public string? DeleterName { get; set; }
        public bool IsDeleted { get; set; } = true;
        public DateTime? DeleteTime { get; set; } = Cores.BeiJingNow;

        public SolftDeleteDto(IUserContext? userContext)
        {
            DeleterId = userContext?.UserId;
            DeleterName = userContext?.UserName;
        }
    }
}
